package com.andova.libyuv

import android.graphics.ImageFormat
import android.graphics.Rect
import android.graphics.YuvImage
import android.media.Image
import android.os.Build
import androidx.annotation.FloatRange
import androidx.annotation.IntRange
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileOutputStream
import java.nio.ByteBuffer
import kotlin.math.roundToInt

@Suppress("FunctionName")
class YUVLegacy {
    companion object {
        /** RGBA图片转YUV420数据
         * 宽、高不能为奇数
         * @param pixels 图片像素集合
         * @param width 宽
         * @param height 高
         * @return
         */
        @Deprecated("use libyuv instead.")
        fun rgbaToYuv420(pixels: IntArray?, width: Int, height: Int): ByteArray? {
            pixels ?: return null
            val len = width * height
            val yuv = ByteArray(len * 3 / 2) //yuv格式数组大小，y亮度占len长度，u,v各占len/4长度。
            var y: Int
            var u: Int
            var v: Int
            for (i in 0 until height) {
                for (j in 0 until width) {
                    //屏蔽ARGB的透明度值
                    val rgb = pixels[i * width + j] and 0x00FFFFFF
                    //像素的颜色顺序为bgr，移位运算。
                    val r = rgb and 0xFF
                    val g = rgb shr 8 and 0xFF
                    val b = rgb shr 16 and 0xFF
                    //套用公式
                    y = (66 * r + 129 * g + 25 * b + 128 shr 8) + 16
                    u = (-38 * r - 74 * g + 112 * b + 128 shr 8) + 128
                    v = (112 * r - 94 * g - 18 * b + 128 shr 8) + 128
                    //调整
                    y = if (y < 16) 16 else if (y > 255) 255 else y
                    u = if (u < 0) 0 else if (u > 255) 255 else u
                    v = if (v < 0) 0 else if (v > 255) 255 else v
                    //赋值
                    yuv[i * width + j] = y.toByte()
                    yuv[len + (i shr 1) * width + (j and 1.inv()) + 0] = u.toByte()
                    yuv[len + +(i shr 1) * width + (j and 1.inv()) + 1] = v.toByte()
                }
            }
            return yuv
        }

        @Deprecated("use libyuv instead.")
        fun rgbToYuv420Sp(pixels: IntArray?, width: Int, height: Int): ByteArray? {
            pixels ?: return null
            val yuv = ByteArray(width * height * 3 / 2)
            var y: Int
            var u: Int
            var v: Int
            var r: Int
            var g: Int
            var b: Int
            var yIndex = 0
            var uvIndex = width * height
            for (i in 0 until height) {
                for (j in 0 until width) {
                    r = (pixels[yIndex] and 0xff0000) shr 16
                    g = (pixels[yIndex] and 0xff00) shr 8
                    b = (pixels[yIndex] and 0xff) shr 0
                    y = (0.299 * r + 0.587 * g + 0.114 * b).toInt()
                    u = (-0.1687 * r - 0.3313 * g + 0.5 * b + 128).toInt()
                    v = (0.5 * r - 0.4187 * g - 0.0813 * b + 128).toInt()
                    yuv[yIndex++] = (if (y < 0) 0 else if (y > 255) 255 else y).toByte()
                    if (i % 2 == 0 && j % 2 == 0) {
                        yuv[uvIndex++] = (if (u < 0) 0 else if (u > 255) 255 else u).toByte()
                        yuv[uvIndex++] = (if (v < 0) 0 else if (v > 255) 255 else v).toByte()
                    }
                }
            }
            return yuv
        }

        @Deprecated("use libyuv instead.")
        fun rgbToYuv420Sp(pixels: ByteArray?, width: Int, height: Int): ByteArray? {
            pixels ?: return null
            val yuv = ByteArray(width * height * 3 / 2)
            var y: Int
            var u: Int
            var v: Int
            var r: Int
            var g: Int
            var b: Int
            var yIndex = 0
            var uvIndex = width * height
            for (i in 0 until height) {
                for (j in 0 until width) {
                    r = pixels[yIndex * 3].toInt()
                    g = pixels[yIndex * 3 + 1].toInt()
                    b = pixels[yIndex * 3 + 2].toInt()
                    y = (0.299 * r + 0.587 * g + 0.114 * b).toInt()
                    u = (-0.1687 * r - 0.3313 * g + 0.5 * b + 128).toInt()
                    v = (0.5 * r - 0.4187 * g - 0.0813 * b + 128).toInt()
                    yuv[yIndex++] = (if (y < 0) 0 else if (y > 255) 255 else y).toByte()
                    if (i % 2 == 0 && j % 2 == 0) {
                        yuv[uvIndex++] = (if (u < 0) 0 else if (u > 255) 255 else u).toByte()
                        yuv[uvIndex++] = (if (v < 0) 0 else if (v > 255) 255 else v).toByte()
                    }
                }
            }
            return yuv
        }

        @Deprecated("use libyuv instead.")
        fun yuvToRgba(bytes: ByteArray?, width: Int, height: Int): IntArray? {
            bytes ?: return null
            val size = width * height
            val rgba = IntArray(size)
            for (i in 0 until height) {
                for (j in 0 until width) {
                    var y = 0xff and bytes[i * width + j].toInt()
                    val u = 0xff and bytes[size + (i shr 1) * width + (j and 1.inv()) + 0].toInt()
                    val v = 0xff and bytes[size + (i shr 1) * width + (j and 1.inv()) + 1].toInt()
                    y = if (y < 16) 16 else y

                    var r = (1.164f * (y - 16) + 1.596f * (v - 128)).roundToInt()
                    var g = (1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128)).roundToInt()
                    var b = (1.164f * (y - 16) + 2.018f * (u - 128)).roundToInt()

                    r = if (r < 0) 0 else if (r > 255) 255 else r
                    g = if (g < 0) 0 else if (g > 255) 255 else g
                    b = if (b < 0) 0 else if (b > 255) 255 else b

                    rgba[i * width + j] = 0xff + (b shl 16) + (g shl 8) + r
                }
            }
            return rgba
        }

        /**
         * 旋转yuv数据90度
         */
        @Deprecated("use libyuv instead.")
        fun rotateYuvDegree90(data: ByteArray?, imageWidth: Int, imageHeight: Int): ByteArray? {
            data ?: return null
            val yuv = ByteArray(imageWidth * imageHeight * 3 / 2)

            var i = 0
            for (x in 0 until imageWidth) { // Rotate the Y luma
                for (y in imageHeight - 1 downTo 0) {
                    yuv[i++] = data[y * imageWidth + x]
                }
            }

            i = imageWidth * imageHeight * 3 / 2 - 1
            var x = imageWidth - 1
            while (x > 0) { // Rotate the U and V color components
                for (y in 0 until imageHeight / 2) {
                    yuv[i--] = data[imageWidth * imageHeight + y * imageWidth + x]
                    yuv[i--] = data[imageWidth * imageHeight + y * imageWidth + (x - 1)]
                }
                x -= 2
            }
            return yuv
        }

        /**
         * 旋转yuv数据270度
         */
        @Deprecated("use libyuv instead.")
        fun rotateYuvDegree270(data: ByteArray?, imageWidth: Int, imageHeight: Int): ByteArray? {
            data ?: return null
            val yuv = ByteArray(imageWidth * imageHeight * 3 / 2)

            var i = 0
            for (x in imageWidth - 1 downTo 0) { // Rotate the Y luma
                for (y in 0 until imageHeight) {
                    yuv[i++] = data[y * imageWidth + x]
                }
            }

            i = imageWidth * imageHeight
            var x = imageWidth - 1
            while (x > 0) { // Rotate the U and V color components
                for (y in 0 until imageHeight / 2) {
                    yuv[i++] = data[imageWidth * imageHeight + y * imageWidth + (x - 1)]
                    yuv[i++] = data[imageWidth * imageHeight + y * imageWidth + x]
                }
                x -= 2
            }
            return yuv
        }

        @Deprecated("use libyuv instead.")
        fun rotateYuvDegree270AndMirror(data: ByteArray?, imageWidth: Int, imageHeight: Int): ByteArray? {
            data ?: return null
            val yuv = ByteArray(imageWidth * imageHeight * 3 / 2)

            var i = 0
            var maxY: Int
            for (x in imageWidth - 1 downTo 0) { // Rotate and mirror the Y luma
                maxY = imageWidth * (imageHeight - 1) + x * 2
                for (y in 0 until imageHeight) {
                    yuv[i++] = data[maxY - (y * imageWidth + x)]
                }
            }

            val uvSize = imageWidth * imageHeight
            i = uvSize
            var maxUV: Int
            var x = imageWidth - 1
            while (x > 0) { // Rotate and mirror the U and V color components
                maxUV = imageWidth * (imageHeight / 2 - 1) + x * 2 + uvSize
                for (y in 0 until imageHeight / 2) {
                    yuv[i++] = data[maxUV - 2 - (y * imageWidth + x - 1)]
                    yuv[i++] = data[maxUV - (y * imageWidth + x)]
                }
                x -= 2
            }
            return yuv
        }

        /**
         * 将Y:U:V == 4:2:2的数据转换为nv21
         *
         * @param src_y      Y 数据
         * @param src_u      U 数据
         * @param src_v      V 数据
         * @param dst_nv21   生成的nv21，需要预先分配内存
         * @param stride 步长
         * @param height 图像高度
         */
        @Deprecated("use libyuv instead.")
        fun YUV422ToNV21(src_y: ByteArray, src_u: ByteArray, src_v: ByteArray, dst_nv21: ByteArray, stride: Int, height: Int) {
            System.arraycopy(src_y, 0, dst_nv21, 0, src_y.size)
            // 注意，若length值为 y.length * 3 / 2 会有数组越界的风险，需使用真实数据长度计算
            val length = src_y.size + src_u.size / 2 + src_v.size / 2
            var uIndex = 0
            var vIndex = 0
            var i = stride * height
            while (i < length) {
                dst_nv21[i] = src_v[vIndex]
                dst_nv21[i + 1] = src_u[uIndex]
                vIndex += 2
                uIndex += 2
                i += 2
            }
        }

        /**
         * 将Y:U:V == 4:1:1的数据转换为nv21
         *
         * @param src_y      Y 数据
         * @param src_u      U 数据
         * @param src_v      V 数据
         * @param dst_nv21   生成的nv21，需要预先分配内存
         * @param stride 步长
         * @param height 图像高度
         */
        @Deprecated("use libyuv instead.")
        fun YUV420ToNV21(src_y: ByteArray, src_u: ByteArray, src_v: ByteArray, dst_nv21: ByteArray, stride: Int, height: Int) {
            System.arraycopy(src_y, 0, dst_nv21, 0, src_y.size)
            // 注意，若length值为 y.length * 3 / 2 会有数组越界的风险，需使用真实数据长度计算
            val length = src_y.size + src_u.size + src_v.size
            var uIndex = 0
            var vIndex = 0
            var i = stride * height
            while (i < length) {
                dst_nv21[i] = src_v[vIndex++]
                dst_nv21[i + 1] = src_u[uIndex++]
                i += 2
            }
        }

        @RequiresApi(Build.VERSION_CODES.KITKAT)
        fun handleImageReader(image: Image): ByteArray? {
            if (image.format != ImageFormat.YUV_420_888) return null
            val planes = image.planes // Y:U:V == 4:2:2
            if (planes.size != 3) return null
            val width = image.width
            val height = image.height
            val nv21 = ByteArray(width * height * 3 / 2)
            val y = ByteArray(planes[0].buffer.limit())
            val u = ByteArray(planes[1].buffer.limit())
            val v = ByteArray(planes[2].buffer.limit())
            planes[0].buffer.get(y)
            planes[1].buffer.get(u)
            planes[2].buffer.get(v)
            when (y.size / u.size) {
                2 -> {
//                    val s = SystemClock.currentThreadTimeMillis()
//                    GlLibYuv.nativeYuv422pToNv21(planes[0].buffer, planes[1].buffer, planes[2].buffer, nv21, width, height)
                    YUV422ToNV21(y, u, v, nv21, width, height)
//                    i("millis -> ${SystemClock.currentThreadTimeMillis() - s}")
                }
                4 -> YUV420ToNV21(y, u, v, nv21, width, height)
                else -> return null
            }
            return nv21
        }

        @Deprecated("use libyuv instead.")
        fun decodeNv21ToJpeg(
            data: ByteArray, file: File, previewWidth: Int, previewHeight: Int,
            @IntRange(from = 1L, to = 100L) quality: Int = 100, rotateDegree: Float = 0f
        ): String? {
            var fos: FileOutputStream? = null
            try {
                var imgW = previewWidth
                var imgH = previewHeight
                val bytes = when (rotateDegree) {
                    90f -> {
                        imgW = previewHeight
                        imgH = previewWidth
                        rotateYuvDegree90(data, previewWidth, previewHeight)
                    }
                    270f -> {
                        imgW = previewHeight
                        imgH = previewWidth
                        rotateYuvDegree270(data, previewWidth, previewHeight)
                    }
                    else -> data
                }
                val yuvImage = YuvImage(bytes, ImageFormat.NV21, imgW, imgH, null)
                fos = FileOutputStream(file)
                yuvImage.compressToJpeg(Rect(0, 0, imgW, imgH), quality, fos)
                return file.path
            } catch (ignore: Exception) {
            } finally {
                fos?.close()
            }
            return null
        }
    }
}

fun abgrToArgb(rgba: ByteArray, width: Int, height: Int): ByteArray {
    val bgra = ByteArray(rgba.size)
    LibYUV.ABGRToARGB(rgba, bgra, width, height)
    return bgra
}

fun scaleYuv(srcYuv: ByteArray?, srcWidth: Int, srcHeight: Int, @FloatRange(from = 0.1, to = 1.0) scale: Float): ByteArray? {
    srcYuv ?: return null
    val dstYuv = ByteArray((scale * srcWidth * srcHeight * 3 / 2).toInt())
    LibYUV.ScaleYUV(srcYuv, srcWidth, srcHeight, dstYuv, scale)
    return dstYuv
}

fun scaleRgba(srcRgba: ByteArray?, srcWidth: Int, srcHeight: Int, @FloatRange(from = 0.1, to = 1.0) scale: Float): ByteArray? {
    if (scale >= 1f) return srcRgba
    srcRgba ?: return null
    val dstRgba = ByteArray((scale * srcWidth * srcHeight * 4).toInt())
    LibYUV.ScaleABGR(srcRgba, srcWidth, srcHeight, dstRgba, scale)
    return dstRgba
}

fun rgbaToNv21(rgba: ByteArray?, width: Int, height: Int, @FloatRange(from = 0.1, to = 1.0) scale: Float): ByteArray? {
    rgba ?: return null
    val yuv = ByteArray((scale * width * height * 3 / 2).toInt())
    if (scale >= 1f) LibYUV.ABGRToNV21(rgba, yuv, width, height) else LibYUV.ScaleABGRToNV21(rgba, yuv, width, height, scale)
    return yuv
}

fun bgraToNv21(bgra: ByteArray?, width: Int, height: Int, @FloatRange(from = 0.1, to = 1.0) scale: Float): ByteArray? {
    bgra ?: return null
    val yuv = ByteArray((scale * width * height * 3 / 2).toInt())
    if (scale >= 1f) LibYUV.ABGRToNV21(bgra, yuv, width, height) else LibYUV.ScaleARGBToNV21(bgra, yuv, width, height, scale)
    return yuv
}

@Suppress("FunctionName")
class LibYUV {
    companion object {
        init {
            System.loadLibrary("yuv")
        }

        external fun ScaleYUV(src_yuv: ByteArray, src_width: Int, src_height: Int, dst_yuv: ByteArray, @FloatRange(from = 0.1, to = 1.0) scale: Float)
        external fun ScaleABGR(src_abgr: ByteArray, src_width: Int, src_height: Int, dst_abgr: ByteArray, @FloatRange(from = 0.1, to = 1.0) scale: Float)

        /**
         * @param src_rgba RRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA
         * @param dst_bgra BBBBBBBBGGGGGGGGRRRRRRRRAAAAAAAA
         */
        external fun ABGRToARGB(src_rgba: ByteArray, dst_bgra: ByteArray, width: Int, height: Int)

        external fun BGRToNV21(src_rgb: ByteArray, dst_yuv: ByteArray, width: Int, height: Int)
        private external fun ScaleBGRToNV21(src_rgb: ByteArray, dst_yuv: ByteArray, width: Int, height: Int, @FloatRange(from = 0.1, to = 1.0) scale: Float)

        /**
         * @param src_rgba RRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA
         */
        external fun ABGRToNV21(src_rgba: ByteArray, dst_yuv: ByteArray, width: Int, height: Int)
        external fun ScaleARGBToNV21(src_rgba: ByteArray, dst_yuv: ByteArray, width: Int, height: Int, @FloatRange(from = 0.1, to = 1.0) scale: Float)
        external fun ScaleABGRToNV21(src_rgba: ByteArray, dst_yuv: ByteArray, width: Int, height: Int, @FloatRange(from = 0.1, to = 1.0) scale: Float)

        @Deprecated("")
        external fun YUVToYUV420(src_y: ByteArray, src_u: ByteArray, src_v: ByteArray, dst_yuv: ByteArray, width: Int, height: Int)
        external fun TestAndroid420ToI420(src_y: ByteArray, src_u: ByteArray, src_v: ByteArray, dst_y: ByteArray, dst_u: ByteArray, dst_v: ByteArray, width: Int, height: Int)
        external fun TestI422ToI420(src_y: ByteArray, src_u: ByteArray, src_v: ByteArray, dst_y: ByteArray, dst_u: ByteArray, dst_v: ByteArray, width: Int, height: Int)
        external fun TestI444ToI420(src_y: ByteArray, src_u: ByteArray, src_v: ByteArray, dst_y: ByteArray, dst_u: ByteArray, dst_v: ByteArray, width: Int, height: Int)
        external fun TestI420ToNV21(src_y: ByteArray, src_u: ByteArray, src_v: ByteArray, dst_yuv: ByteArray, width: Int, height: Int)
        external fun TestNV21ToI420(src_y: ByteArray, dst_y: ByteArray, dst_u: ByteArray, dst_v: ByteArray, width: Int, height: Int)
        external fun TestHalfMergeUVPlane(src_u: ByteArray, src_v: ByteArray, dst_uv: ByteArray, width: Int, height: Int)

        external fun YUV420ToNV21(src_y: ByteBuffer, src_u: ByteBuffer, src_v: ByteBuffer, dst_yuv: ByteArray, width: Int, height: Int)

        @Deprecated("")
        external fun YUV422ToNV21(src_y: ByteBuffer, src_u: ByteBuffer, src_v: ByteBuffer, dst_yuv: ByteArray, angle: Int, width: Int, height: Int)
        external fun GetByteArray(src: ByteBuffer, len: Int): ByteArray?
        external fun RotateNV21(src: ByteArray, dst: ByteArray, angle: Int, width: Int, height: Int)
    }
}

@Suppress("FunctionName")
class TestNEON {
    companion object {
        init {
            System.loadLibrary("testNEON")
        }

        external fun TestNEON_VTRN(src_u: ByteArray, src_v: ByteArray, dst_uv: ByteArray, stride: Int)
        external fun TestHalfMerge(src_u: ByteArray, src_v: ByteArray, dst_uv: ByteArray, stride: Int)
        external fun TestNEON_VPADDL(src_u: ByteArray, src_v: ByteArray, dst_u: ByteArray, dst_v: ByteArray, stride: Int)
        external fun TestNEON_VPADAL(src_u: ByteArray, src_v: ByteArray, dst_u: ByteArray, dst_v: ByteArray, stride: Int)
        external fun TestNEON_VQRSHRN(src_u: ByteArray, src_v: ByteArray, dst_u: ByteArray, dst_v: ByteArray, stride: Int)
    }
}